﻿Imports System.Reflection
Imports System.IO
Imports System.Text

Namespace Util

    ''' <summary>
    ''' リソース取得用ユーティリティクラス。
    ''' </summary>
    ''' <history>[sonodar] 2011/06/24 新規作成</history>
    Public NotInheritable Class ResourceUtil

        ''' <summary>
        ''' デフォルトコンストラクタを不可視とすることで、<br/>
        ''' インスタンスの生成を禁止する。
        ''' </summary>
        Private Sub New()
            '// 何もしない    
        End Sub

#Region "アセンブリからリソース取得"
        ''' <summary>
        ''' アセンブリ内からファイル名に該当するリソースを検索し、リストで返す。
        ''' </summary>
        ''' <param name="asmbly">リソース取得元のアセンブリ</param>
        ''' <param name="fileName">検索するファイル名</param>
        ''' <param name="onlyFirst">
        ''' 最初のファイルが見つかった時点で処理を終了するかどうかのフラグ<br/>
        ''' Trueを指定した場合は要素数1のリストを返す
        ''' </param>
        ''' <returns>
        ''' リソース情報のリスト<br/>
        ''' 見つからなかった場合は要素数0のリストインスタンス
        ''' </returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Overloads Shared Function FindResource(ByVal asmbly As Assembly, ByVal fileName As String, _
                                            Optional ByVal onlyFirst As Boolean = False) As IList(Of ManifestResourceInfo)
            '// 返却リストを初期化
            Dim list As IList(Of ManifestResourceInfo) = New List(Of ManifestResourceInfo)

            '// アセンブリ内のリソースすべてを検索
            For Each name As String In asmbly.GetManifestResourceNames

                '// ファイル名が後方一致した場合は返却リストに追加
                If (name.EndsWith(fileName)) Then

                    list.Add(asmbly.GetManifestResourceInfo(name))

                    '// 1件のみフラグが指定されていたら処理終了
                    If (onlyFirst) Then
                        Return list
                    End If

                End If
            Next

            Return list

        End Function

        ''' <summary>
        ''' アセンブリのディレクトリを取得する。
        ''' </summary>
        ''' <param name="assmbly">アセンブリ</param>
        ''' <returns>アセンブリのディレクトリパス文字列</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Shared Function GetAssemblyDirectoryName(ByVal assmbly As Assembly) As String
            Return Path.GetDirectoryName(assmbly.Location)
        End Function

        ''' <summary>
        ''' 実行EXEのディレクトリを取得する。
        ''' </summary>
        ''' <returns>アセンブリのディレクトリパス文字列</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Shared Function GetEntryAssemblyDirectoryName() As String
            Return GetAssemblyDirectoryName(Assembly.GetEntryAssembly)
        End Function

#End Region

#Region "ファイルシステムからリソース取得"

        ''' <summary>
        ''' ファイルシステムからファイルのStreamを取得する。<br/>
        ''' isFullPathにTrueが指定されていた場合は絶対パスとして取得。<br/>
        ''' 省略した場合は実行EXEフォルダからの相対パスでファイルのStreamを取得する。
        ''' </summary>
        ''' <param name="fileName">ファイル名</param>
        ''' <param name="isFullPath">
        ''' 絶対パスかどうか<br/>
        ''' 省略した場合はFalse（実行EXEからの相対パス）
        ''' </param>
        ''' <returns>リソースのStream</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Overloads Shared Function GetResourceStream(ByVal fileName As String, _
                                                 Optional ByVal isFullPath As Boolean = False) _
                                                 As Stream
            '// 絶対パス
            If (isFullPath) Then
                Return New FileStream(fileName, FileMode.Open, FileAccess.Read)
            End If

            '// 実行EXEからの相対パス
            Dim path As String = StringUtil.Join(IO.Path.DirectorySeparatorChar, _
                                                 GetEntryAssemblyDirectoryName, _
                                                 fileName)
            Return New FileStream(path, FileMode.Open, FileAccess.Read)

        End Function

        ''' <summary>
        ''' 指定ディレクトリ内からファイル名に該当するファイルを検索し、リストで返す。
        ''' </summary>
        ''' <param name="fileName">検索するファイル名</param>
        ''' <param name="dirPath">検索するフォルダ</param>
        ''' <param name="onlyFirst">
        ''' 最初のファイルが見つかった時点で処理を終了するかどうかのフラグ<br/>
        ''' Trueを指定した場合は要素数1のリストを返す
        ''' </param>
        ''' <returns>
        ''' ファイル情報のリスト<br/>
        ''' 見つからなかった場合は要素数0のリストインスタンス
        ''' </returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Overloads Shared Function FindResource(ByVal fileName As String, _
                                            Optional ByVal dirPath As String = Nothing, _
                                            Optional ByVal onlyFirst As Boolean = False) As IList(Of FileInfo)
            '// 返却リストを初期化
            Dim list As IList(Of FileInfo) = New List(Of FileInfo)

            '// 指定パス配下のファイルをすべてを検索
            SearchDirectory(fileName, New DirectoryInfo(dirPath), onlyFirst, list)

            Return list

        End Function

        ''' <summary>
        ''' 指定ディレクトリ内でファイル名に一致するファイルを再帰的に取得する。
        ''' </summary>
        ''' <param name="fileName">検索するファイル名</param>
        ''' <param name="dir">検索するフォルダ</param>
        ''' <param name="onlyFirst">
        ''' 最初のファイルが見つかった時点で処理を終了するかどうかのフラグ<br/>
        ''' Trueを指定した場合は要素数1のリストを返す
        ''' </param>
        ''' <param name="list">ファイル情報のリスト</param>
        ''' <remarks></remarks>
        <System.Diagnostics.DebuggerStepThrough()> _
        Private Shared Sub SearchDirectory(ByVal fileName As String, _
                                           ByVal dir As DirectoryInfo, _
                                           ByVal onlyFirst As Boolean, ByRef list As IList(Of FileInfo))

            '// 存在しないフォルダの場合は終了
            If (Not dir.Exists) Then
                Return
            End If

            '// 1件のみの取得でいい場合は処理終了
            If (onlyFirst AndAlso list.Count > 0) Then
                Return
            End If

            '// ディレクトリ内のファイルをすべて検索
            For Each file As FileInfo In dir.GetFiles
                If (file.Name.Equals(fileName)) Then
                    list.Add(file)
                    If (onlyFirst) Then
                        Return
                    End If
                End If
            Next

            '// サブディレクトリに対して再帰処理
            For Each subDir As DirectoryInfo In dir.GetDirectories
                SearchDirectory(fileName, subDir, onlyFirst, list)
            Next

        End Sub

#End Region

        ''' <summary>
        ''' Key=Value形式の設定ファイルを読み込み、キーと値のMapを返す。
        ''' </summary>
        ''' <param name="resource">設定ファイルのStream</param>
        ''' <param name="textEncoding">文字エンコーディング</param>
        ''' <returns>キーと値のMap</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Shared Function LoadConfigFile(ByVal resource As Stream, ByVal textEncoding As Encoding) As IDictionary(Of String, String)

            '// 設定ファイルの情報を保持するMap
            Dim resourceMap As IDictionary(Of String, String) = New Dictionary(Of String, String)

            '// 設定ファイルをUTF-8で読み込む
            Using reader As StreamReader = New StreamReader(resource, Encoding.UTF8)

                Dim lineNo As Integer = 0

                '// 設定ファイルを最後まで読み込む
                While (Not reader.EndOfStream)

                    Dim line As String = reader.ReadLine().Trim()

                    lineNo += 1

                    '// 空行、またはコメント行は飛ばす
                    If (CommonUtil.IsEmpty(line) OrElse line.StartsWith(CharConsts.Number)) Then
                        Continue While
                    End If

                    Dim equalIndex As Integer = line.IndexOf(CharConsts.Equal)

                    '// "="がない場合はエラー
                    If (equalIndex = -1) Then
                        Throw New System.Exception(lineNo & "行目: Key=Valueの形式で設定してください。")
                    End If

                    Dim key As String = Left$(line, equalIndex)
                    Dim value As String = line.Substring(equalIndex + 1)

                    '// キーがすでにある場合はエラー
                    If (resourceMap.ContainsKey(key)) Then
                        Throw New System.Exception(lineNo & "行目: キーが重複しています。Key=" & key)
                    End If

                    '// キーと値をMapに追加
                    resourceMap.Add(key, value)

                End While

            End Using

            Return resourceMap

        End Function

    End Class

End Namespace